package com.jnks.service.impl;


import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jnks.constant.R;
import com.jnks.constant.enums.GeneralStatusEnum;
import com.jnks.mapper.*;
import com.jnks.pojo.domain.dto.StrikeDeviceRequestDTO;
import com.jnks.pojo.domain.vo.*;
import com.jnks.pojo.entity.ArmedzoneDeviceLocationTDO;
import com.jnks.pojo.entity.ArmedzoneDeviceStrikeTDO;
import com.jnks.pojo.entity.ArmedzoneDeviceWorkStatusTDO;
import com.jnks.pojo.entity.DeviceStrikeLogTDO;
import com.jnks.service.BaseInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.*;

@Service
public class BaseInfoServiceImpl implements BaseInfoService {
    @Autowired
    private ArmedzoneDeviceTMapper deviceTMapper;
    @Autowired
    private ArmedzoneDeviceWorkStatusTMapper armedzoneDeviceWorkStatusTMapper;
    @Autowired
    private ArmedzoneDeviceLocationTMapper armedzoneDeviceLocationTMapper;
    @Autowired
    private DeviceStrikeLogTMapper deviceStrikeLogTMapper;
    @Autowired
    private ArmedzoneDeviceStrikeTMapper armedzoneDeviceStrikeTMapper;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private static ExecutorService executorService = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

//    private final static String host = "http://10.2.1.42:9803";
    private final static String host = "http://127.0.0.1:9803";


    @Override
    public List<OneArmedzoneDeviceResponseVo> listAllDevice() {

        List<OneArmedzoneDeviceResponseVo> oneArmedzoneDeviceResponseVos = deviceTMapper.listAllDevice();
        return oneArmedzoneDeviceResponseVos;
    }


    @Override
    public ArrayList<ArmedzoneDeviceAngleResponseVo> deviceAngle(DeviceAngleRequestVo requestVo) {
        ArrayList<ArmedzoneDeviceAngleResponseVo> armedzoneDeviceAngleResponseVos = new ArrayList<>();

        if (requestVo.getDeviceIds() != null) {
            for (DeviceAngleRequestVo.DeviceAngle deviceAngle : requestVo.getDeviceIds()) {
                ArmedzoneDeviceAngleResponseVo armedzoneDeviceAngleResponseVo = new ArmedzoneDeviceAngleResponseVo();
                ArmedzoneDeviceWorkStatusTDO armedzoneDeviceWorkStatusTDO = armedzoneDeviceWorkStatusTMapper.selectOne(
                        new LambdaQueryWrapper<ArmedzoneDeviceWorkStatusTDO>().eq(ArmedzoneDeviceWorkStatusTDO::getDeviceId, deviceAngle.getDeviceId()));
                if (armedzoneDeviceWorkStatusTDO != null) {
                    armedzoneDeviceAngleResponseVo.setDeviceId(armedzoneDeviceWorkStatusTDO.getDeviceId());
                    armedzoneDeviceAngleResponseVo.setHorizontal_angle(
                            armedzoneDeviceWorkStatusTDO.getHorizontal_heading_angle() == null ? "0" : armedzoneDeviceWorkStatusTDO.getHorizontal_heading_angle().toString());
                    armedzoneDeviceAngleResponseVo.setVertical_angle(
                            armedzoneDeviceWorkStatusTDO.getVertical_heading_angle() == null ? "0" : armedzoneDeviceWorkStatusTDO.getVertical_heading_angle().toString());
                    armedzoneDeviceAngleResponseVo.setNorthbound_angle(
                            armedzoneDeviceWorkStatusTDO.getNorthboundAngle() == null ? "0" : armedzoneDeviceWorkStatusTDO.getNorthboundAngle().toString());
                    armedzoneDeviceAngleResponseVos.add(armedzoneDeviceAngleResponseVo);
                } else {
                    armedzoneDeviceAngleResponseVo.setDeviceId(deviceAngle.getDeviceId());
                    armedzoneDeviceAngleResponseVo.setHorizontal_angle("0");
                    armedzoneDeviceAngleResponseVo.setVertical_angle("0");
                    armedzoneDeviceAngleResponseVo.setNorthbound_angle("0");
                    armedzoneDeviceAngleResponseVos.add(armedzoneDeviceAngleResponseVo);
                }
            }

        }
        return armedzoneDeviceAngleResponseVos;
    }

    @Override
    public ArmedzoneDeviceLocationTDO getDeviceLoc(Integer deviceId) {
        return armedzoneDeviceLocationTMapper.selectOne(new LambdaQueryWrapper<ArmedzoneDeviceLocationTDO>().eq(ArmedzoneDeviceLocationTDO::getDeviceId, deviceId));
    }

    @Override
    public R deviceUpdate(OneArmedzoneDeviceResponseVo requestVos) {
        return null;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public R strike(CallDeviceRequsetVo vo) {
        /**
         * @description: 调用设备开启接口 判断 Redis是否有该设备
         * 有该设备 返回 设备开启中，请勿重复开启 提示语
         * 无该设备 在 Redis 中存设备 id，60s 倒计时结束后从 Redis 中移除
         */
        //构造参数
        String key = String.format("STRIKE_DEVICE_CALL@ID_%s_CITY_%s", vo.getDeviceId(), vo.getCityId());
        Set<String> listKey = redisTemplate.keys(("STRIKE_DEVICE_CALL@ID_" + vo.getDeviceId()).concat("*"));
        if (!CollectionUtils.isEmpty(listKey) && vo.getStrikeCircuit() != 2) return R.data("201", null, "设备已开启，运行中");
        StrikeDeviceRequestDTO harware = deviceTMapper.queryHarwareByDeviceId(vo.getDeviceId());
        if (Objects.isNull(harware)) return R.data("201", null, "该设备不存在或已停用");
        harware.setStrikeType(vo.getStrikeType());
        harware.setAutoStrike(GeneralStatusEnum.NO.getIntCode());
        harware.setStrikeTime(new Date());
        harware.setStrikeCircuit(vo.getStrikeCircuit());
        harware.setStrikeSeconds(vo.getStrikeSeconds());
        StrikeInfoVO strikeInfoVO = new StrikeInfoVO();
        strikeInfoVO.setStrikeVO(buildStrikeVO(harware));
        strikeInfoVO.setDeviceStrike(buildDeviceStrike(harware));

        //通过接口形式调用反制设备
        String path = "/sys/strike/strike";
        String url = host + path;
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> a = new HttpEntity<>(JSONObject.toJSONString(strikeInfoVO), headers);
        System.out.println(a);
        ResponseEntity<String> postForEntity = restTemplate.postForEntity(url, a, String.class);
        JSONObject jsonObject = JSONObject.parseObject(postForEntity.getBody());
        System.out.println("jsonObject====================" + jsonObject.get("code"));
        if (!jsonObject.get("code").equals(200)) {
            return R.data(jsonObject.get("code").toString(), null, jsonObject.get("message").toString());
        }

        // 设备开启记录入库
        strikeInfoStoreInDB(harware, 1);
        CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(1000L * harware.getStrikeSeconds());
                strikeInfoStoreInDB(harware, 0);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, executorService);

        redisTemplate.opsForValue().set(key, new Date(), vo.getStrikeSeconds(), TimeUnit.SECONDS);

        return R.data("指令下发成功");
    }

    @Override
    public R auto(NewDroneReportVO vo) {

        String path = "/sys/cloud/auto";
        String url = host + path;

        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<String> a = new HttpEntity<>(JSONObject.toJSONString(vo), headers);

        ResponseEntity<String> postForEntity = restTemplate.postForEntity(url, a, String.class);
        JSONObject jsonObject = JSONObject.parseObject(postForEntity.getBody());
        if (!jsonObject.get("code").equals(200)) {
            return R.data(jsonObject.get("code").toString(), null, jsonObject.get("message").toString());
        }

        return R.data("手动指引成功");
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public R openTrace(TrackVO vo) {
        ArmedzoneDeviceStrikeTDO armedzoneDeviceStrikeTDO = armedzoneDeviceStrikeTMapper.selectOne(new LambdaQueryWrapper<ArmedzoneDeviceStrikeTDO>().eq(ArmedzoneDeviceStrikeTDO::getDeviceId, vo.getDeviceId()));

        armedzoneDeviceStrikeTDO.setTrackDisable(vo.getTrackDisable());

        armedzoneDeviceStrikeTMapper.updateById(armedzoneDeviceStrikeTDO);
        return R.data("操作成功");
    }

    /**
     * @description: 创建 ArmedzoneDeviceStrikeTDO 对象
     * @author: jbllin
     * @date: 2022/11/9
     */
    private StrikeVO buildStrikeVO(StrikeDeviceRequestDTO dto) {
        return StrikeVO.builder()
                .autoStrike(0)
                .deviceId(dto.getDeviceId())
                .strikeSeconds(dto.getStrikeSeconds())
                .strikeTime(new Date())
                .strikeType(dto.getStrikeType())
                .strikeUser(dto.getStrikeUser())
                .strikeCircuit(Objects.isNull(dto.getStrikeCircuit()) ? 1 : dto.getStrikeCircuit())
                .build();
    }

    /**
     * @description: 创建 DeviceStrike 对象
     * @author: jbllin
     * @date: 2022/11/9
     */
    private ArmedzoneDeviceStrikeVO buildDeviceStrike(StrikeDeviceRequestDTO dto) {
        return ArmedzoneDeviceStrikeVO.builder()
                .id(dto.getArmedzoneDeviceStrikeId())
                .deviceId(dto.getDeviceId())
                .interfaceType(dto.getInterfaceType())
                .strikeInfo(dto.getStrikeInfo())
                .remark(dto.getRemark())
                .status(dto.getStatus())
                .strikeSeconds(dto.getStrikeSeconds())
                .build();
    }

    private void strikeInfoStoreInDB(StrikeDeviceRequestDTO harware, Integer status) {
        DeviceStrikeLogTDO deviceStrikeLogTDO = new DeviceStrikeLogTDO();
        deviceStrikeLogTDO.setDeviceId(harware.getDeviceId());
        deviceStrikeLogTDO.setType(status);
        deviceStrikeLogTDO.setStrikeTime(new Date());
        deviceStrikeLogTDO.setStrikeType(harware.getStrikeType());
        deviceStrikeLogTDO.setAutoStrike(harware.getAutoStrike());
        deviceStrikeLogTDO.setStrikeSeconds(harware.getStrikeSeconds());
        deviceStrikeLogTDO.setStrikeUser(harware.getStrikeUserId());
        deviceStrikeLogTDO.setCompanyId(harware.getCompanyId());
        deviceStrikeLogTDO.setTypeId(harware.getDeviceType());
        deviceStrikeLogTMapper.insert(deviceStrikeLogTDO);
    }

}
